package QuoteHTMLFetcher;

use LWP; # loads lots of necessary classes.
use Encode;
use HTML::TreeBuilder 3; 
use HTML::Element;
use WWW::Mechanize;
use Data::Dumper;
use StockInfo;

sub new {
	my $self = shift;
	$self->{PROVIDERS} = {};
	$self->{BROWSER} = undef;	
	return $self;
}
##########################################################
## this method set/get the provider information			##
## parameters for set action:							##
## 		$provider_code	the provider code				##
##		$format		the information format 				##
##					{ FORMAT=> {HaSTC =>[], HoSTC=>[]}, ##
##					  URLS => {HaSTC =>"", HoSTC=>""},	##
##					  PRIORITY => 						##	
##					}									##
## parameters for get action:							##
## 		$provider_code	the provider code				##
##	return for get action:								##
##		A provider whose code is $provider_code			##
##########################################################	
sub providers {	
	my $self = shift;
	my $provider_code = shift;
	my $format = shift;

	if(ref($format)){
		$self->{PROVIDERS}->{$provider_code} = $format;	
	}else{
		return ($self->{PROVIDERS}->{$provider_code});
	}
}

sub do_GET {
	  my $self = shift;	  
	  $self->{BROWSER} = WWW::Mechanize->new(
	  	agent => 'Mozilla/5.0 (compatible; MSIE 4.01; Windows95)'	  	
	  ) unless $self->{BROWSER};
	  my $browser = $self->{BROWSER};
	  $browser->parse_head(0);	  
	  my $resp = $browser->get(@_);  
	  return ($resp->content, $resp->status_line, $resp->is_success, $resp)
	    if wantarray;
	  return unless $resp->is_success;
	  return $resp->content;
}

######################################################################
## this method fetches all stock information 						##
## from all provider and all center									##	
## parameters:														##
## 		$scode		the stock code									##
##	return:															##	
##		$total_results		the number of found StockInfo objects	##
##		$stock_info_objects	the array of all found StockInfo objects##
######################################################################				
sub fetch_stock_info{
	my $self = shift;
	my $scode = shift;
	my $count = 0;		
	my $total_results = 0;
	my @stock_info_objects;
		
	foreach my $provider_code (keys %{$self->{PROVIDERS}}){		
		last if $total_results;
		($total_results, @stock_info_objects) = $self->fetch_stock_info_by_provider($scode, $provider_code);				
	}	
	return ($total_results, @stock_info_objects) if wantarray;
	return $total_results;
}

######################################################################
## this method fetches all stock information from a security center	##
## such as 'HaSTC' or 'HoSTC'										##
## parameters:														##
## 		$s_code	the stock code										##
##		$center	the security center code							##
## return:															##
##		$total_results		the number of found StockInfo objects	##
##		$stock_info_objects	the array of all found StockInfo objects##
######################################################################

sub fetch_stock_info_by_center{
	my $self = shift;
	my $s_code = shift;	
	my $center = shift;
	my $total_results = 0;
	my @stock_info_objects;
	
	#@@ need review
	if($center ne 'HoSTC' && $center ne 'HaSTC'){
		return ($total_results, @stock_info_objects) if wantarray;
		return $total_results;
	}	
	foreach my $provider_code (keys %{$self->{PROVIDERS}}){
		last if $total_results;
		($count, $stock_info) = $self->fetch_stock_info_by_url($s_code, $self->{PROVIDERS}->{$provider_code}->{'URLS'}->{$center});
		if($count){
			$total_results += $count;
			$all_stock_info .= $stock_info;
			if($total_results){
				# split $all_stock_info to a html array
				@html_results = split(/\|/,$all_stock_info);	
				# convert the html array to stock info objects
				foreach my $html_result (@html_results){				
					push @stock_info_objects, $self->convert_html_to_object($html_result, $provider_code, $center);
				}
			}
		}	
	} 
	return ($total_results, @stock_info_objects) if wantarray;
	return $total_results;
}

######################################################################
## this method fetches all stock infomation from a provider			##
## such as 'GOV', 'SSI', and so on									##
## parameters:														##
##		$s_code				the stock code							##
##		$provider_code		the provider code						##
##	return:															##	
##		$total_results		the number of found StockInfo objects	##
##		$stock_info_objects	the array of all found StockInfo objects##
######################################################################				
sub fetch_stock_info_by_provider{	
	my $self = shift;	
	my $all_stock_info = '';	
	my $total_results = 0;
	my @html_results =[]; 
	my @stock_info_objects;
	my ($s_code, $provider_code) = @_;	
	
	# get provider return;
	my $provider = $self->providers($provider_code);	
	if(!ref($provider)){
		return ($total_results, @stock_info_objects) if wantarray;
		return $total_results;
	}
	
	# find in all centers
	my @centers = keys %{$provider->{'URLS'}};
	foreach my $center (@centers){
		my $count = 0;
		my $stock_info = '';		
		($count, $stock_info) = $self->fetch_stock_info_by_url($s_code,$provider->{'URLS'}->{$center});
		if($count){
			$total_results += $count;
			# split $all_stock_info to a html array
			@html_results = split(/\|/,$stock_info);	
			# convert the html array to stock info objects
			foreach my $html_result (@html_results){
				push @stock_info_objects, $self->convert_html_to_object($html_result, $provider_code, $center);
			}
		}
	}	
	return ($total_results, @stock_info_objects) if wantarray;
	return $total_results;
}

##########################################################
## this method fetches the stock info mation from a url ##
## parameters:											##
##			$s_code		the stock code					##
##			$url		the url							##
## return:												##
##			$count		the number of results			##
##			$stock_info the html string that			##
##						concatenates all html results   ##
##						by '|' character				##
##########################################################
sub fetch_stock_info_by_url{
	my $self = shift;
	my $s_code = shift;
	my $url = shift;
	my $count = 0;	
	my $stock_info = '';
	my ($html, $status, $is_success) = $self->do_GET($url);
	if (!$is_success){
		return ($count, $stock_info) if wantarray;
		return $count;	
	}		
	my $root = HTML::TreeBuilder->new_from_content(Encode::decode_utf8($html));
	$root->eof();
	# If the page has chkAll option, checked it and resubmit form		
		
	my @tables = $root->find_by_tag_name('table');
	# Find the table that has maximum number of childs
	my $table_idx = 0;
	my $max_children_number = 0;
	my $idx = 0;
	for ($idx=0; $idx< @tables; $idx++){
		my @child_nodes = $tables[$idx]->content_list();
		if(uc($child_nodes[0]->tag()) eq "TBODY" ){
			$tables[$idx] = $child_nodes[0];
			@child_nodes = $tables[$idx]->content_list();
		}		
		if(@child_nodes > $max_children_number){
			$max_children_number = @child_nodes;
			$table_idx = $idx;			
		}
	}
	if($max_children_number > 0){
		#print "The largest table is " . $table_idx . " and the index is " . $idx . " with " . $max_children_number ." children \n";				
		foreach my $tr_node ($tables[$table_idx]->content_list){
			if($tr_node->as_text() =~ /$s_code/){
				$count ++ ;	
				$stock_info .= $tr_node->as_HTML();
				# add trailing symbol to the stock info string 
				$stock_info .= "|";				
			}
		}		
	}
	
	#$root->dump;
	$root->delete();	
	return ($count, $stock_info) if wantarray;
	return $count;
}

##############################################################
## this method convert a html string containing information ##
## about a stock quote to a StockInfo object				##
## parameters:												##
## 		$html 			the html source						##
##		$provider_code	the security company				##
##		$center			the security center (HaSTC, HoSTC)	##
## returns:													##
##		@stock_info_objects	the array of StockInfo objects	##  
##############################################################
sub convert_html_to_object{
	my $self = shift;
	my $html = shift;
	my $provider_code = (shift);
	my $center = (shift);
	
	#print $provider_code . "--" . $center . "--" . "$html\n" ;
	my $stock_object = StockInfo->new();
	# create a html tree
	my $html_root = HTML::TreeBuilder->new_from_content($html);
	# objectify text this tree
	$html_root->objectify_text();
	# get all ~text nodes 
	my @tds = $html_root->find_by_tag_name('td');
	# empty <td></td> will be replace by <td><~text 'text'='_EMPTY'></~text></td>
	foreach my $td (@tds){
		if($td->is_empty()){			
			$td->replace_with(HTML::Element->new('~text', 'text' => '_EMPTY_'));
			$td->delete;
		}
	}
	my @texts = $html_root->find_by_tag_name('~text');	
	my @num_info = @{$self->{PROVIDERS}->{$provider_code}->{FORMAT}->{$center}};
	#print @texts . " -- " . @num_info . "\n";
	for(my $i = 0; $i < @num_info; $i++){		
		my $field = $self->{PROVIDERS}->{$provider_code}->{FORMAT}->{$center}[$i];
		if($texts[$i]->attr('text') ne '_EMPTY_'){ 				
			$stock_object->set($field, $texts[$i]->attr('text'));
		}else{ # if the field text attribute was replaced by '_EMPTY_' set it ""
			$stock_object->set($field, "");
		}		
	}
	$html_root->delete();
	$stock_object->set("_PROVIDER", $provider_code);
	$stock_object->set("_CENTER", $center);
	#print Dumper($stock_object) ."\n";
	return $stock_object;	
}

########################
## the debug function ##
########################
sub debug {
	my $self = shift;
}

1;
